let fs = require('fs');

function createLoaderObject(loader) {
  let loaderObj = {
    path:loader,//loader的绝对路径
    normal: null,//loader函数本身
    pitch: null,//loader的pitch函数
    raw: false,//是否要转成字符串 raw=true表示传递给loader是一个buffer,加载图片的 默认值是false,表示把内容转成字符串后才传递给loader
    data: {},//每一个loader都会自有一个自定义数据对象，用来存一些自定义信息
    pitchExecuted: false,//这个loader的pitch方法是不是已经 执行这了
    normalExecuted: false,//这个loader的normal方法是不是已经 执行过
  }
  return loaderObj;
}
function processResource(options, loaderContext, callback) {
  loaderContext.loaderIndex = loaderContext.loaders.length - 1;debugger
  options.readResource(loaderContext.resource, function (err, buffer) {
    options.resourceBuffer = buffer;
    iterateNormalLoaders(options, loaderContext, [buffer], callback);
  });
}
/**
 * 从右向左执行normal函数
 * @param {*} processOptions 选项对象
 * @param {*} loaderContext 上下文对象
 * @param {*} args 上一个loader传递给我的参数
 * @param {*} finalCallback 最终的回调
 * @returns 
 */
function iterateNormalLoaders(options, loaderContext, args, callback) {
  if (loaderContext.loaderIndex < 0)
    return callback(null, args);
  var currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex];
  if (currentLoaderObject.normalExecuted) {
    loaderContext.loaderIndex--;
    return iterateNormalLoaders(options, loaderContext, args, callback);
  }
  var fn = currentLoaderObject.normal;
  currentLoaderObject.normalExecuted = true;
  if (!fn) {
    return iterateNormalLoaders(options, loaderContext, args, callback);
  }
  convertArgs(args, currentLoaderObject.raw);
  //args传给iterateNormalLoaders的时候都要转成数组
  runSyncOrAsync(fn, loaderContext, args, function (err,...args) {
    if (err) return callback(err);
    iterateNormalLoaders(options, loaderContext, args, callback);
  });
}
function convertArgs(args, raw) {
  if (raw && !Buffer.isBuffer(args[0])) {
    args[0] = Buffer.from(args[0]);//如果这个normal函数想要buffer,但是参数不是Buffer
  } else if (!raw && Buffer.isBuffer(args[0])) {//想要字符串，但是是个buffer
    args[0] = args[0].toString('utf8');
  }
}

function iteratePitchingLoaders(options, loaderContext, callback) {
  if (loaderContext.loaderIndex >= loaderContext.loaders.length) {
    return processResource(options, loaderContext, callback);
  }
  let currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex];
  if (currentLoaderObject.pitchExecuted) {
    loaderContext.loaderIndex++;
    return iteratePitchingLoaders(options, loaderContext, callback);
  }
  loadLoader(currentLoaderObject, () => {
    let fn = currentLoaderObject.pitch;
    currentLoaderObject.pitchExecuted = true;//表示pitch函数已经执行过了
    if (!fn) {//如果说此loader没有提供pitch方法
      return iteratePitchingLoaders(options, loaderContext, callback);
    }
    //以同步或者异步的方式运行
    runSyncOrAsync(fn, loaderContext,
      [loaderContext.remainingRequest, loaderContext.previousRequest, loaderContext.data = {}],
      (err,...args) => {
        if (args.length > 0) {
          loaderContext.loaderIndex--;
          iterateNormalLoaders(options, loaderContext, args, callback);
        } else {
          iteratePitchingLoaders(options, loaderContext, callback);
        }
      }
    );
  });
}
function loadLoader(loader, callback) {
  var module = require(loader.path);
  loader.normal = module;
  loader.pitch = module.pitch;
  loader.raw = module.raw;
  callback();
}
function runSyncOrAsync(fn, context, args, callback) {
  var isSync = true;
  var isDone = false;
  context.async = function async() {
    isSync = false;
    return innerCallback;
  };
  var innerCallback = context.callback = function (...args) {
    isDone = true;
    isSync = false;
    callback.apply(null, args);
  };
  var result = fn.apply(context, args);
  if (isSync) {
    isDone = true;
    if (result === undefined)
      return callback();
    return callback(null, result);
  }
}

function runLoaders(options, callback) {
  let resource = options.resource;//获取 要到加载的资源 src/index.js
  let loaders = options.loaders || [];//要经过哪些loader进行处理
  let loaderContext = options.context || {};//loader执行上下文
  let readResource = options.readResource || fs.readFile;//读取文件内容的方法
  //把每个loader从一个loader 绝对路径默认成一个loader对象
  let loaderObjects = loaders.map(createLoaderObject);
  loaderContext.resource = resource;
  loaderContext.readResource = readResource;
  loaderContext.loaderIndex = 0;//当前 正在执行的loader的索引 0 1 2
  loaderContext.loaders = loaderObjects;//loader对象的数组
  loaderContext.callback = null;
  loaderContext.async = null;
  Object.defineProperty(loaderContext, 'request', {
    get() {
      return loaderContext.loaders.map(l => l.request).concat(loaderContext.resource).join('!')
    }
  });
  Object.defineProperty(loaderContext, 'remainingRequest', {
    get() {
      return loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(l => l.request).concat(loaderContext.resource).join('!')
    }
  });
  Object.defineProperty(loaderContext, 'currentRequest', {
    get() {
      return loaderContext.loaders.slice(loaderContext.loaderIndex).map(l => l.request).concat(loaderContext.resource).join('!')
    }
  });
  Object.defineProperty(loaderContext, 'previousRequest', {
    get() {
      return loaderContext.loaders.slice(0, loaderContext.loaderIndex).map(l => l.request).join('!')
    }
  });
  Object.defineProperty(loaderContext, 'data', {
    get() {
      return loaderContext.loaders[loaderContext.loaderIndex].data;
    }
  });
  let processOptions = {
    resourceBuffer: null, //要读取的资源的二进制内容 转换前的要加载的文件的内容 
    readResource
  }
  iteratePitchingLoaders(processOptions, loaderContext, (err, result) => {
    callback(err, {
      result,
      resourceBuffer: processOptions.resourceBuffer
    });
  });
}

exports.runLoaders = runLoaders;